home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ADA Programming Guide
/
ADA Programming Guide.iso
/
ada_demo
/
bd3.ada
next >
Wrap
Text File
|
1996-01-30
|
20KB
|
541 lines
package BANK is
--------------------------------------------------------------------------
--| BEGIN PROLOGUE
--| DESCRIPTION : BANK defines a bank, the TELLER objects
--| : within it, and the procedure PRINT_REPORT
--| : (which reports on the status of the BANK).
--| :
--| : BANK is an abstract state machine, defining
--| : a BANK object which contains TELLER objects.
--| :
--| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
--| : object-oriented design and tasking
--| : with Ada
--| :
--| LIMITATIONS : None
--| AUTHOR(S) : Richard Conn (RLC)
--| CHANGE LOG : 1/16/89 RLC Initial Design and Code
--| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
--| REMARKS : None
--| PORTABILITY ISSUES : None
--| END PROLOGUE
--------------------------------------------------------------------------
-- TRANSACTION requested of a TELLER
type TRANSACTION is (ADD_NEW_CUSTOMER,
GET_BALANCE,
MAKE_DEPOSIT,
MAKE_WITHDRAWAL);
-- Unit of currency
type DOLLAR is new FLOAT;
-- Identification of a CUSTOMER
type CUSTOMER_ID is new NATURAL range 1 .. NATURAL'LAST;
-- Index of and Number of TELLER objects within the bank
type TELLER_INDEX is new NATURAL range 1 .. 4;
-- A TELLER_PERSON is an object to which a CUSTOMER may make a REQUEST
-- The BANK tells the TELLER_PERSON to START_WORK, giving the
-- TELLER_PERSON its TELLER_NUMBER
task type TELLER_PERSON is
entry REQUEST(ID : in out CUSTOMER_ID;
KIND : in TRANSACTION;
AMOUNT : in out DOLLAR);
end TELLER_PERSON;
-- These are the TELLER objects available at the bank
TELLER : array(TELLER_INDEX) of TELLER_PERSON;
-- PRINT_REPORT gives the transaction log of all the bank
-- customers
procedure PRINT_REPORT;
-- STOP_WORK terminates all TELLER tasks
procedure STOP_WORK;
end BANK;
--
with CONSOLE;
package body BANK is
--------------------------------------------------------------------------
--| BEGIN PROLOGUE
--| DESCRIPTION : Package Body BANK implements the TELLER
--| : tasks and the PRINT_REPORT procedure.
--| : CUSTOMER data is maintained within the
--| : BANK as a linked list.
--| :
--| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
--| : object-oriented design and tasking
--| : with Ada
--| :
--| LIMITATIONS : None
--| AUTHOR(S) : Richard Conn (RLC)
--| CHANGE LOG : 1/16/89 RLC Initial Design and Code
--| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
--| REMARKS : None
--| PORTABILITY ISSUES : Uses CONSOLE (TEXT_IO), so is very portable;
--| : no known portability problems.
--| END PROLOGUE
--------------------------------------------------------------------------
-- Identifier of next customer
NEXT_ID : CUSTOMER_ID := CUSTOMER_ID'FIRST;
-- Linked list of customer data
type CUSTOMER_DATA;
type CUSTOMER_DATA_POINTER is access CUSTOMER_DATA;
type CUSTOMER_DATA is record
ID : CUSTOMER_ID;
BALANCE : DOLLAR := 0.0;
TRANSACTION_COUNT : NATURAL := 0;
ATTEMPTED_OVERDRAWS : NATURAL := 0;
NEXT : CUSTOMER_DATA_POINTER := null;
end record;
-- Count of number of transactions for each TELLER object
TELLER_TRANSACTION_COUNT : array(TELLER_INDEX) of NATURAL
:= (others => 0);
-- Pointers to first and last customer data entries
FIRST_CUSTOMER : CUSTOMER_DATA_POINTER := null;
LAST_CUSTOMER : CUSTOMER_DATA_POINTER := null;
--
-- package body BANK
-- Print a report of the status of the BANK
procedure PRINT_REPORT is
CURRENT_CUSTOMER : CUSTOMER_DATA_POINTER;
begin
-- Check for any customers and issue an error message if none
if NEXT_ID = CUSTOMER_ID'FIRST then
CONSOLE.WRITE("The bank doesn't have any customers");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
-- Generate report
else
-- Customer balance, transaction count, attempted overdraw
-- count report
CONSOLE.WRITE("**** BANK STATUS REPORT ****");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CONSOLE.WRITE(
"Customer Balance Transactions Attempted_Overdraws");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CURRENT_CUSTOMER := FIRST_CUSTOMER;
while CURRENT_CUSTOMER /= null loop
CONSOLE.WRITE(INTEGER(CURRENT_CUSTOMER.ID), 5);
CONSOLE.WRITE(" ");
CONSOLE.WRITE(FLOAT(CURRENT_CUSTOMER.BALANCE), 8, 2);
CONSOLE.WRITE(" ");
CONSOLE.WRITE(INTEGER(CURRENT_CUSTOMER.TRANSACTION_COUNT), 8);
CONSOLE.WRITE(" ");
CONSOLE.WRITE(INTEGER(CURRENT_CUSTOMER.ATTEMPTED_OVERDRAWS),
8);
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CURRENT_CUSTOMER := CURRENT_CUSTOMER.NEXT;
end loop;
CONSOLE.WRITE(CONSOLE.NEW_LINE);
-- Teller transaction count report
CONSOLE.WRITE("Teller : ");
for I in TELLER_INDEX loop
CONSOLE.WRITE(INTEGER(I), 8);
end loop;
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CONSOLE.WRITE("Transaction_Count: ");
for I in TELLER_INDEX loop
CONSOLE.WRITE(INTEGER(TELLER_TRANSACTION_COUNT(I)), 8);
end loop;
CONSOLE.WRITE(CONSOLE.NEW_LINE);
end if;
end PRINT_REPORT;
-- Terminate all TELLER tasks
procedure STOP_WORK is
begin
for I in TELLER_INDEX loop
abort TELLER(I);
end loop;
end STOP_WORK;
--
-- package body BANK
-- FIND_CUSTOMER is used to find a CUSTOMER_DATA record
-- based on a CUSTOMER_ID number
function FIND_CUSTOMER(ID : in CUSTOMER_ID)
return CUSTOMER_DATA_POINTER is
CURRENT_CUSTOMER : CUSTOMER_DATA_POINTER;
begin
CURRENT_CUSTOMER := FIRST_CUSTOMER;
while CURRENT_CUSTOMER /= null loop
exit when CURRENT_CUSTOMER.ID = ID;
CURRENT_CUSTOMER := CURRENT_CUSTOMER.NEXT;
end loop;
return CURRENT_CUSTOMER;
end FIND_CUSTOMER;
--
-- package body BANK
task TELLER_ASSIGNER is
-- This task assigns an ID number to a teller.
-- TELLER_ASSIGNER is called by the TELLER_PERSON task when the
-- TELLER_PERSON task first starts up.
entry GET_TELLER_ID(ID : out TELLER_INDEX);
end TELLER_ASSIGNER;
task body TELLER_ASSIGNER is
NEXT_TELLER_ID : TELLER_INDEX := TELLER_INDEX'FIRST;
begin
loop
accept GET_TELLER_ID(ID : out TELLER_INDEX) do
ID := NEXT_TELLER_ID;
if NEXT_TELLER_ID /= TELLER_INDEX'LAST then
NEXT_TELLER_ID := NEXT_TELLER_ID + 1;
end if;
end GET_TELLER_ID;
end loop;
end TELLER_ASSIGNER;
--
-- package body BANK
-- Implementation of a TELLER task
task body TELLER_PERSON is
THIS_CUSTOMER : CUSTOMER_DATA_POINTER;
MY_NUMBER : TELLER_INDEX;
begin
-- TELLER gets his ID number
TELLER_ASSIGNER.GET_TELLER_ID(MY_NUMBER);
-- TELLER loops on REQUESTs from CUSTOMERs
loop
accept REQUEST(ID : in out CUSTOMER_ID;
KIND : in TRANSACTION;
AMOUNT : in out DOLLAR) do
-- Increment teller's transaction count
TELLER_TRANSACTION_COUNT(MY_NUMBER)
:= TELLER_TRANSACTION_COUNT(MY_NUMBER) + 1;
--
-- package body BANK
-- Process REQUEST
case KIND is
when ADD_NEW_CUSTOMER =>
if LAST_CUSTOMER = null then
LAST_CUSTOMER := new CUSTOMER_DATA;
FIRST_CUSTOMER := LAST_CUSTOMER;
else
LAST_CUSTOMER.NEXT := new CUSTOMER_DATA;
LAST_CUSTOMER := LAST_CUSTOMER.NEXT;
end if;
LAST_CUSTOMER.ID := NEXT_ID;
ID := NEXT_ID;
NEXT_ID := NEXT_ID + 1;
THIS_CUSTOMER := LAST_CUSTOMER;
when GET_BALANCE =>
THIS_CUSTOMER := FIND_CUSTOMER(ID);
AMOUNT := THIS_CUSTOMER.BALANCE;
when MAKE_DEPOSIT =>
THIS_CUSTOMER := FIND_CUSTOMER(ID);
THIS_CUSTOMER.BALANCE
:= THIS_CUSTOMER.BALANCE + AMOUNT;
AMOUNT := THIS_CUSTOMER.BALANCE;
when MAKE_WITHDRAWAL =>
THIS_CUSTOMER := FIND_CUSTOMER(ID);
if THIS_CUSTOMER.BALANCE < AMOUNT then
AMOUNT := 0.0;
THIS_CUSTOMER.ATTEMPTED_OVERDRAWS :=
THIS_CUSTOMER.ATTEMPTED_OVERDRAWS + 1;
else
THIS_CUSTOMER.BALANCE
:= THIS_CUSTOMER.BALANCE - AMOUNT;
end if;
end case;
-- Increment CUSTOMER's transaction count
THIS_CUSTOMER.TRANSACTION_COUNT
:= THIS_CUSTOMER.TRANSACTION_COUNT + 1;
end REQUEST;
end loop;
end TELLER_PERSON;
end BANK;
--
package CUSTOMER_WORLD is
--------------------------------------------------------------------------
--| BEGIN PROLOGUE
--| DESCRIPTION : CUSTOMER_WORLD is an abstract state machine
--| : which defines the collection of all CUSTOMERs
--| : of the BANK. It allows the mainline procedure
--| : to ADD a new CUSTOMER and TERMINATE_ALL
--| : current CUSTOMERs. The CUSTOMER itself acts
--| : as an independent task and requires no
--| : direct interaction with the mainline once
--| : it starts.
--| :
--| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
--| : object-oriented design and tasking
--| : with Ada
--| :
--| LIMITATIONS : None
--| AUTHOR(S) : Richard Conn (RLC)
--| CHANGE LOG : 1/16/89 RLC Initial Design and Code
--| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
--| REMARKS : None
--| PORTABILITY ISSUES : None
--| END PROLOGUE
--------------------------------------------------------------------------
-- Add another CUSTOMER task to the system
procedure ADD;
-- Terminate all CUSTOMERs in the system
procedure TERMINATE_ALL;
end CUSTOMER_WORLD;
--
with BANK;
with RANDOM;
package body CUSTOMER_WORLD is
-- Support infix operations, like DOLLAR < DOLLAR
use BANK;
-- This is the amount that a CUSTOMER initially deposits
-- into his account
INITIAL_DEPOSIT : constant BANK.DOLLAR := 100.0;
-- A CUSTOMER is a task, running concurrently with the TELLERs,
-- other CUSTOMERs, and the mainline task
task type CUSTOMER;
-- CUSTOMER_POINTER is a pointer to a CUSTOMER, used to
-- create new customers and track old customers in the linked
-- list of customers used for task termination when the
-- program is complete
type CUSTOMER_POINTER is access CUSTOMER;
-- These are the constructs used to create a linked list of
-- the customers
type CUSTOMER_LIST_ELEMENT;
type CUSTOMER_LIST_ELEMENT_POINTER is access CUSTOMER_LIST_ELEMENT;
type CUSTOMER_LIST_ELEMENT is record
THIS_CUSTOMER : CUSTOMER_POINTER;
NEXT : CUSTOMER_LIST_ELEMENT_POINTER := null;
end record;
-- Pointer to the first of the customers
FIRST_CUSTOMER : CUSTOMER_LIST_ELEMENT_POINTER := null;
LAST_CUSTOMER : CUSTOMER_LIST_ELEMENT_POINTER := null;
--
-- package body CUSTOMER_WORLD
-- Implementation of the CUSTOMER task
task body CUSTOMER is
ID : BANK.CUSTOMER_ID := BANK.CUSTOMER_ID'FIRST;
BALANCE : BANK.DOLLAR := 0.0;
AMOUNT : BANK.DOLLAR := 0.0;
MY_TRANSACTION : BANK.TRANSACTION;
-- SELECT_TELLER makes a random selection of one of the
-- BANK's tellers
function SELECT_TELLER return BANK.TELLER_INDEX is
begin
return BANK.TELLER_INDEX(
RANDOM.NUMBER * FLOAT(BANK.TELLER_INDEX'LAST) + 1.0);
exception
when others =>
return BANK.TELLER_INDEX'LAST;
end SELECT_TELLER;
begin -- CUSTOMER activity
-- As a new CUSTOMER, the first two transactions of the
-- CUSTOMER are to ask the TELLER to add him as a new
-- CUSTOMER and make an initial deposit
BANK.TELLER(SELECT_TELLER).REQUEST(ID, BANK.ADD_NEW_CUSTOMER,
BALANCE);
BALANCE := INITIAL_DEPOSIT;
BANK.TELLER(SELECT_TELLER).REQUEST(ID, BANK.MAKE_DEPOSIT, BALANCE);
-- The rest of the life of the CUSTOMER is spent in the
-- these steps within the following loop:
-- (1) delay a random amount of time from 0 to 5 seconds
-- (2) compute a random amount from -$50 to +$50
-- (3) if the random amount is negative, attempt to
-- withdraw that amount; if positive, deposit that
-- amount
loop
delay DURATION(5.0 * RANDOM.NUMBER);
AMOUNT := BANK.DOLLAR(100.0 * RANDOM.NUMBER - 50.0);
if AMOUNT < DOLLAR'(0.0) then
MY_TRANSACTION := BANK.MAKE_WITHDRAWAL;
AMOUNT := -AMOUNT;
else
MY_TRANSACTION := BANK.MAKE_DEPOSIT;
end if;
BANK.TELLER(SELECT_TELLER).REQUEST(ID, MY_TRANSACTION, AMOUNT);
end loop;
end CUSTOMER;
--
-- package body CUSTOMER_WORLD
-- Add a new CUSTOMER to the linked list
procedure ADD is
begin
-- If the list is empty, start a new list
if LAST_CUSTOMER = null then
FIRST_CUSTOMER := new CUSTOMER_LIST_ELEMENT;
LAST_CUSTOMER := FIRST_CUSTOMER;
-- If the list is not empty, add an element onto it
else
LAST_CUSTOMER.NEXT := new CUSTOMER_LIST_ELEMENT;
LAST_CUSTOMER := LAST_CUSTOMER.NEXT;
end if;
-- Start a new CUSTOMER task
LAST_CUSTOMER.THIS_CUSTOMER := new CUSTOMER;
end ADD;
-- Terminate all CUSTOMER tasks by moving thru the linked list
-- and explicitly terminating the tasks pointed to by the list
-- elements.
procedure TERMINATE_ALL is
CURRENT_CUSTOMER : CUSTOMER_LIST_ELEMENT_POINTER;
begin
CURRENT_CUSTOMER := FIRST_CUSTOMER;
while CURRENT_CUSTOMER /= null loop
abort CURRENT_CUSTOMER.THIS_CUSTOMER.all;
CURRENT_CUSTOMER := CURRENT_CUSTOMER.NEXT;
end loop;
end TERMINATE_ALL;
end CUSTOMER_WORLD;
--
with BANK;
with CUSTOMER_WORLD;
with CONSOLE;
procedure BD3 is -- BANK_DEMO_3
--------------------------------------------------------------------------
--| BEGIN PROLOGUE
--| DESCRIPTION : Procedure BD3 (BANK DEMO 3) is a mainline
--| : which demonstrates the operation of the
--| : BANK. Upon invocation, the console becomes
--| : a command processor for the bank manager.
--| : The bank manager can obtain the status of
--| : the bank (balances, number of transactions,
--| : and attempted overdraws of all customers
--| : and number of transactions processed by all
--| : tellers) in a single or continuous (group
--| : of 11 over about one minute) display.
--| : The user at the console can also cause new
--| : Customer tasks to be created and shut down
--| : the system.
--| :
--| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
--| : object-oriented design and tasking
--| : with Ada
--| :
--| LIMITATIONS : None
--| AUTHOR(S) : Richard Conn (RLC)
--| CHANGE LOG : 1/16/89 RLC Initial Design and Code
--| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
--| REMARKS : None
--| PORTABILITY ISSUES : Uses CONSOLE (TEXT_IO), so is very portable;
--| : no known portability problems.
--| END PROLOGUE
--------------------------------------------------------------------------
-- Line input from the console
INPUT : CONSOLE.OUTSTRING;
-- Number of continuous status reports displayed by the 'c'
-- command before control is returned to the console
NUMBER_OF_CONTINUOUS_STATUS_REPORTS : constant := 10;
--
-- procedure BD3
begin -- mainline
-- This is the beginning of the main loop. In this loop,
-- a list of commands is printed on the console, the user
-- at the console (as CUSTOMER 0) enters one of these commands
-- followed by striking the RETURN key, and the command is
-- processed.
loop
-- Command listing and prompt
CONSOLE.WRITE("Enter");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CONSOLE.WRITE(" b for bank status");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CONSOLE.WRITE(" c for continuous bank status");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CONSOLE.WRITE(" s for start next customer");
CONSOLE.WRITE(CONSOLE.NEW_LINE);
CONSOLE.WRITE(" x for exit: ");
CONSOLE.READ(INPUT);
-- Interpretation and execution of input command
case INPUT(1) is
when 'b' | 'c' => -- Short or continuous bank status
-- report
BANK.PRINT_REPORT;
if INPUT(1) = 'c' then
for I in 1 .. NUMBER_OF_CONTINUOUS_STATUS_REPORTS loop
delay 5.0;
BANK.PRINT_REPORT;
CONSOLE.WRITE(CONSOLE.NEW_LINE);
end loop;
end if;
when 's' => -- Start up a new CUSTOMER
CUSTOMER_WORLD.ADD;
when 'x' => -- Exit program
CUSTOMER_WORLD.TERMINATE_ALL; -- Kill CUSTOMER tasks
BANK.STOP_WORK; -- Kill TELLER tasks
exit; -- Exit loop
when ' ' => -- Non-error on a null input line
null;
when others => -- Other commands are invalid
CONSOLE.WRITE("Invalid Command: ");
CONSOLE.WRITE(CONSOLE.TRIM(INPUT));
CONSOLE.WRITE(CONSOLE.NEW_LINE);
end case;
end loop;
end BD3;